#include <QApplication>  
#include <QWidget>  
#include <QPushButton>  
#include <QSlider>  
#include <QFileDialog>  
#include <QVBoxLayout>  
#include <QDebug>  
#include <QtWidgets>

//opencv
#include <opencv2/opencv.hpp>  
#include <opencv2/highgui/highgui.hpp>  

using namespace std;  

// 全局变量，用于存储ROI的坐标  
bool selectingROI_ = false;  
cv::Point startPoint_, endPoint_;  
  
// 鼠标事件处理函数  
void mouseHandler(int event, int x, int y, int flags, void* param)  
{  
    cv::Mat& image = *(cv::Mat*)param;  
    cv::Mat image_draw = image.clone();
    cv::Size image_size=image_draw.size();

    switch (event)  
    {  
    case cv::EVENT_LBUTTONDOWN:  
        // 鼠标左键按下时，记录开始点  
        startPoint_ = cv::Point(x, y);  
        selectingROI_ = true;  
        break;  
  
    case cv::EVENT_MOUSEMOVE:  
        // 鼠标移动时，如果正在绘制ROI，则更新方框  
        if (selectingROI_)  
        {  
            endPoint_ = cv::Point(x, y);  
            if (endPoint_.x > image_size.width)
                endPoint_.x = image_size.width;
            if (endPoint_.y > image_size.height)
                endPoint_.y = image_size.height;
            // 使用rectangle绘制方框  
            imshow("Image", image);  
            rectangle(image_draw, startPoint_, endPoint_, cv::Scalar(255, 0, 0), 2);  
            imshow("Image", image_draw);  
        }  
        break;  
  
    case cv::EVENT_LBUTTONUP:  
        // 鼠标左键释放时，记录结束点，并结束绘制  
        if (selectingROI_)  
        {  
            selectingROI_ = false;  
            image = image(cv::Rect(startPoint_,endPoint_));
        }  
        break;  
    }  
}  
  
class FilePickerWidget : public QWidget {  
public:  
    cv::Mat image_raw_, roi_image_,gray_image_;
    bool selectingROI_ = false;
    double gaussian_kernel_size_ = 3;
    double gaussian_sigma_ = 0;
    double canny_low_threshold_ = 100;
    double canny_high_threshold_ = 200;

    FilePickerWidget() {  
        // 创建布局  
        QVBoxLayout *layout = new QVBoxLayout(this);  
        resize(400,400);
  
        // 创建按钮并添加到布局中  
        QPushButton *select_file_button = new QPushButton("选择文件", this);  
        layout->addWidget(select_file_button);  
        // 连接按钮的点击信号到槽函数  
        connect(select_file_button, &QPushButton::clicked, this, &FilePickerWidget::onButtonClicked);  

        // 创建一个QLabel对象  
        QLabel *gaussian_kernel_size_label = new QLabel(this);  
        layout->addWidget(gaussian_kernel_size_label);  
        // 设置要显示的文本  
        gaussian_kernel_size_label->setText("定义高斯核的大小，必须是奇数");  
        // 设置文本对齐方式（例如居中）  
        gaussian_kernel_size_label->setAlignment(Qt::AlignLeft);  
        // 调整QLabel的大小（可选）  
        gaussian_kernel_size_label->resize(200, 10);  //这个可能要设置到同一个widge中

        // 创建文本输入框
        QLineEdit *gaussian_kernel_size_lineEdit = new QLineEdit(this);
        layout->addWidget(gaussian_kernel_size_lineEdit);  
        // 设置文本输入框的位置和大小
        gaussian_kernel_size_lineEdit->setGeometry(10, 10, 400, 30);
        // 设置文本输入框的样式和外观
        gaussian_kernel_size_lineEdit->setText("3");
        gaussian_kernel_size_lineEdit->setPlaceholderText("定义高斯核的大小，必须是奇数");
        gaussian_kernel_size_lineEdit->setReadOnly(false);
        // 连接文本输入框的信号与槽函数
        connect(gaussian_kernel_size_lineEdit, &QLineEdit::textChanged, [&](const QString &q_text){
            if(!q_text.isEmpty()){
                string temp = q_text.toStdString();
                int kernel_size = stoi(temp);
                if(kernel_size%2==0){
                    gaussian_kernel_size_ = kernel_size-1;
                }else{
                    gaussian_kernel_size_ = kernel_size;
                }
            }else{
                cout<<"输入为空，错误"<<endl;
            }
            qDebug() << "高斯核的大小:" << q_text;
        });

        // 创建一个QLabel对象  
        QLabel *gaussian_sigma_label = new QLabel(this);  
        layout->addWidget(gaussian_sigma_label);  
        // 设置要显示的文本  
        gaussian_sigma_label->setText("定义高斯核函数的标准差，如果为0，则从ksize计算");  
        // 设置文本对齐方式（例如居中）  
        gaussian_sigma_label->setAlignment(Qt::AlignLeft);  
        // 调整QLabel的大小（可选）  
        gaussian_sigma_label->resize(200, 10);  

        // 创建文本输入框
        QLineEdit *gaussian_sigma_lineEdit = new QLineEdit(this);
        layout->addWidget(gaussian_sigma_lineEdit);  
        // 设置文本输入框的位置和大小
        gaussian_sigma_lineEdit->setGeometry(10, 10, 400, 30);
        // 设置文本输入框的样式和外观
        gaussian_sigma_lineEdit->setText("0");
        gaussian_sigma_lineEdit->setPlaceholderText("定义高斯核函数的标准差，如果为0，则从ksize计算");
        gaussian_sigma_lineEdit->setReadOnly(false);
        // 连接文本输入框的信号与槽函数
        connect(gaussian_sigma_lineEdit, &QLineEdit::textChanged, [&](const QString &q_text){
            if(!q_text.isEmpty()){
                string temp = q_text.toStdString();
                gaussian_sigma_ = stod(temp);
            }else{
                cout<<"输入为空，错误"<<endl;
            }
            qDebug() << "高斯核函数的标准差:" << q_text;
        });

        // 创建按钮并添加到布局中  
        QPushButton *gaussian_button = new QPushButton("高斯滤波", this);  
        layout->addWidget(gaussian_button);  
        // 连接按钮的点击信号到槽函数  
        connect(gaussian_button, &QPushButton::clicked, this, &FilePickerWidget::gaussianButton);  

        // 添加一个空间项来控制间距    控制以上所有控件的距离
        // 这里的 20 是间距的大小，你可以根据需要调整  
        layout->addItem(new QSpacerItem(20, 20, QSizePolicy::Minimum, QSizePolicy::Expanding)); 

        // 创建按钮并添加到布局中  
        QPushButton *binary_button = new QPushButton("二值化", this);  
        layout->addWidget(binary_button);  
        // 连接按钮的点击信号到槽函数  
        connect(binary_button, &QPushButton::clicked, this, &FilePickerWidget::binaryButtonClicked);  

        // 创建滑动条对象
        QSlider* binary_slider = new QSlider(Qt::Horizontal);
        layout->addWidget(binary_slider);  
        // 设置滑动条的取值范围
        binary_slider->setMinimum(0);
        binary_slider->setMaximum(255);
        // 设置初始值
        binary_slider->setValue(50);
        // 连接滑动事件的信号与槽函数
        connect(binary_slider, &QSlider::valueChanged, this, &FilePickerWidget::binarySlider);

        // 创建按钮并添加到布局中  
        QPushButton *canny_button = new QPushButton("canny边缘检测", this);  
        layout->addWidget(canny_button);  
        // 连接按钮的点击信号到槽函数  
        connect(canny_button, &QPushButton::clicked, this, &FilePickerWidget::cannyButtonClicked);  

        // 创建滑动条对象
        QSlider* canny_low_slider = new QSlider(Qt::Horizontal);
        layout->addWidget(canny_low_slider);  
        // 设置滑动条的取值范围
        canny_low_slider->setMinimum(0);
        canny_low_slider->setMaximum(255);
        // 设置初始值
        canny_low_slider->setValue(100);
        // 连接滑动事件的信号与槽函数
        connect(canny_low_slider, &QSlider::valueChanged, this, &FilePickerWidget::cannyLowSlider);

        // 创建滑动条对象
        QSlider* canny_high_slider = new QSlider(Qt::Horizontal);
        layout->addWidget(canny_high_slider);  
        // 设置滑动条的取值范围
        canny_high_slider->setMinimum(0);
        canny_high_slider->setMaximum(255);
        // 设置初始值
        canny_high_slider->setValue(200);
        // 连接滑动事件的信号与槽函数
        connect(canny_high_slider, &QSlider::valueChanged, this, &FilePickerWidget::cannyHighSlider);
    }

private slots:  
    void onButtonClicked() {  
        // 创建文件对话框  
        QString filePath = QFileDialog::getOpenFileName(this, "选择文件", QString(), "所有文件 (*)");  
        // 显示图像  
        cv::namedWindow("Image", cv::WINDOW_AUTOSIZE);  
        // 检查是否有文件被选择  
        if (!filePath.isEmpty()) {  
            qDebug() << "选择的文件路径：" << filePath;  
            // 显示图像 
            image_raw_ = cv::imread(filePath.toStdString());
            if (!image_raw_.empty())  
            {  
                cv::imshow("Image", image_raw_);  
                roi_image_ = image_raw_.clone(); // 创建一个深度拷贝
                // 设置鼠标回调函数  
                cv::setMouseCallback("Image", mouseHandler,&roi_image_);  
                cv::waitKey(0);
                // 显示ROI  
                cv::namedWindow("ROI", cv::WINDOW_AUTOSIZE);  
                cv::imshow("ROI", roi_image_);  
                cv::destroyWindow("Image");
            }  
            else  
            {  
                cout << "Could not open or find the image" << endl;  
            }  
        }  
    }  

    void binaryButtonClicked(){
        cv::Mat gray_image;
        // 将RGB图像转换为灰度图像
        cv::cvtColor(roi_image_, gray_image_, cv::COLOR_BGR2GRAY);
        // 声明变量来存储二值化后的图像和阈值  
        double thresholdValue = 50; // 你可以设置这个阈值  
        // 应用固定阈值二值化  
        cv::threshold(gray_image_, gray_image, thresholdValue, 255, cv::THRESH_BINARY);  
        // 显示原图像和二值化后的图像  
        cv::imshow("Binary Image", gray_image);  
    }

    void binarySlider(int value){
        cv::Mat gray_image;
        // 声明变量来存储二值化后的图像和阈值  
        double thresholdValue = value; // 你可以设置这个阈值  
        // 应用固定阈值二值化  
        cv::threshold(gray_image_, gray_image, thresholdValue, 255, cv::THRESH_BINARY);  
        // 显示原图像和二值化后的图像  
        cv::imshow("Binary Image", gray_image);  
    }

    void gaussianButton(){
        // 创建一个新的Mat对象来存储滤波后的图像  
        cv::Mat blurredImage;  
        // 定义高斯核的大小，必须是奇数  
        cv::Size ksize(gaussian_kernel_size_, gaussian_kernel_size_);  
        // 应用高斯滤波  
        cv::GaussianBlur(roi_image_, blurredImage, ksize, gaussian_sigma_);  
        cv::imshow("Blurred Image", blurredImage);  
    }

    void cannyButtonClicked(){
        // 应用Canny边缘检测  
        cv::Mat edges;  
        cv::Canny(gray_image_, edges, canny_low_threshold_, canny_high_threshold_); // 第一个阈值通常是低阈值，第二个阈值是高阈值  
        cv::imshow("Edges", edges);  
    }

    void cannyLowSlider(int value){
        cv::Mat edges;
        // 声明变量来存储二值化后的图像和阈值  
        if(value<canny_high_threshold_)
            canny_low_threshold_ = value; // 你可以设置这个阈值  
        else
            canny_low_threshold_ = canny_high_threshold_ - 1;
        // 应用固定阈值二值化  
        cv::Canny(gray_image_, edges, canny_low_threshold_, canny_high_threshold_); // 第一个阈值通常是低阈值，第二个阈值是高阈值  
        // 显示原图像和二值化后的图像  
        cv::imshow("Edges", edges);  
    }

    void cannyHighSlider(int value){
        cv::Mat edges;
        // 声明变量来存储二值化后的图像和阈值  
        if(value>canny_low_threshold_)
            canny_high_threshold_ = value; // 你可以设置这个阈值  
        else
            canny_high_threshold_ = canny_low_threshold_ +1;
        // 应用固定阈值二值化  
        cv::Canny(gray_image_, edges, canny_low_threshold_, canny_high_threshold_); // 第一个阈值通常是低阈值，第二个阈值是高阈值  
        // 显示原图像和二值化后的图像  
        cv::imshow("Edges", edges);  
    }

};  
  
int main(int argc, char *argv[]) {  
    QApplication app(argc, argv);  
  
    FilePickerWidget widget;  
    widget.show();  
  
    return app.exec();  
}  